package com.jasonchoi.security.commons.config;

import com.jasonchoi.security.commons.security.authentication.JWTAuthorizationFilter;
import com.jasonchoi.security.commons.security.authentication.LoginFailureHandler;
import com.jasonchoi.security.commons.security.authentication.LoginSuccessHandler;
import com.jasonchoi.security.commons.security.authentication.LoginedAccessDeniedHandler;
import com.jasonchoi.security.commons.security.authentication.NotLoginAccessDeniedHandler;
import com.jasonchoi.security.commons.security.authorization.UrlRoleAuthHandler;
import com.jasonchoi.security.commons.security.authorization.UrlRolesFilterHandler;
import com.jasonchoi.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: JasonChoi
 * @Date: 2020/1/2 16:29
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private JWTAuthorizationFilter jwtAuthorizationFilter;
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    @Autowired
    private UrlRolesFilterHandler urlRolesFilterHandler;

    @Resource
    private UserService userService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //开启跨域 关闭csrf
        http.cors().and().csrf().disable();

        //使用jwt  关闭session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);

        // 登录配置
        http.formLogin().loginProcessingUrl("/login")
            .successHandler(loginSuccessHandler)
            .failureHandler(new LoginFailureHandler())
            .and().logout().permitAll();

        // 授权配置
        http.authorizeRequests()
            .accessDecisionManager(accessDecisionManager())
            .withObjectPostProcessor(new DefinedObjectPostProcessor())
            .anyRequest().authenticated();

        /**
         * 权限不足处理
         * AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常
         * AccessDeineHandler   用来解决认证过的用户访问无权限资源时的异常
         */
        http.exceptionHandling()
            .accessDeniedHandler(new LoginedAccessDeniedHandler())
            .authenticationEntryPoint(new NotLoginAccessDeniedHandler());
    }

    class DefinedObjectPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {
        @Override
        public <O extends FilterSecurityInterceptor> O postProcess(O object) {
            object.setSecurityMetadataSource(urlRolesFilterHandler);
            return object;
        }
    }

    /**
     * AffirmativeBased – 任何一个AccessDecisionVoter返回同意则允许访问
     * ConsensusBased – 同意投票多于拒绝投票（忽略弃权回答）则允许访问
     * UnanimousBased – 每个投票者选择弃权或同意则允许访问
     *
     * 决策管理
     */
    private AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
        decisionVoters.add(new WebExpressionVoter());
        decisionVoters.add(new AuthenticatedVoter());
        decisionVoters.add(new RoleVoter());
        /* 路由权限管理 */
        decisionVoters.add(new UrlRoleAuthHandler());
        return new UnanimousBased(decisionVoters);
    }

}
